Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / domain / src / commonMain / kotlin / org / meshtastic / core / domain / usecase / session / EnsureRemoteAdminSessionUseCase.kt

Displaying Raw • Download

core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/session/EnsureRemoteAdminSessionUseCase.kt 492afbde5c98f3efdcd367ab2f13ef3665fe7388 (492afbde) Text, 4.82 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.domain.usecase.session

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CoroutineStart
Tff7b72import T7ee787kotlinx.coroutines.Deferred
Tff7b72import T7ee787kotlinx.coroutines.async
Tff7b72import T7ee787kotlinx.coroutines.flow.filter
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.sync.Mutex
Tff7b72import T7ee787kotlinx.coroutines.sync.withLock
Tff7b72import T7ee787kotlinx.coroutines.withTimeoutOrNull
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.common.di.ServiceScope
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.model.SessionStatus
Tff7b72import T7ee787org.meshtastic.core.repository.RadioController
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceRepository
Tff7b72import T7ee787org.meshtastic.core.repository.SessionManager
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds

T8b949e/**
* Ensures a remote-admin session exists for the target node, dispatching a metadata request and awaiting a refreshed
* passkey if necessary.
*
* Why this exists: the firmware embeds an 8-byte rotating passkey in every admin response and rejects admin traffic
* lacking a fresh key (`firmware/src/modules/AdminModule.cpp:1460-1481`). Before this use case the UI silently tunneled
* the user into a remote-admin screen that immediately failed if no metadata had been requested first.
*
* Concurrency model:
* - One in-flight ensure per `destNum`. Concurrent callers dedupe onto the same `Deferred` so a double-tap doesn't
* blast two metadata requests at the radio.
* - The refresh-flow subscription is established **before** the metadata request is dispatched to avoid losing the
* response on the inherently raceful `MutableSharedFlow`.
* - The `withTimeoutOrNull` is a UX deadline only — late responses still update the durable `SessionStatus` flow that
* the UI observes, so a "Timeout" outcome here can self-heal in the chip without re-tapping.
*/
Tf0883e@Single
Tff7b72open Tff7b72class T56d364EnsureRemoteAdminSessionUseCaseTb4b4b4(
Tff7b72private Tff7b72val Te6edf3sessionManagerTb4b4b4: Te6edf3SessionManagerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceRepositoryTb4b4b4: Te6edf3ServiceRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceScopeTb4b4b4: Te6edf3ServiceScopeTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3mutex Tff7b72= Te6edf3MutexTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3inFlight Tff7b72= Te6edf3mutableMapOfTff7b72<Tffa657IntTb4b4b4, Te6edf3DeferredTff7b72<Te6edf3EnsureSessionResultTff7b72>Tff7b72>Tb4b4b4(Tb4b4b4)

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tff7b72open Tff7b72suspend Tff7b72operator Tff7b72fun Td2a8ffinvokeTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3EnsureSessionResult Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3value Tff7b72!Tff7b72= Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3EnsureSessionResultTb4b4b4.Te6edf3Disconnected
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3sessionManagerTb4b4b4.Te6edf3observeSessionStatusTb4b4b4(Te6edf3destNumTb4b4b4)Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4) Tff7b72is Te6edf3SessionStatusTb4b4b4.Te6edf3ActiveTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3EnsureSessionResultTb4b4b4.Te6edf3AlreadyActive
Tb4b4b4}

Tff7b72val Te6edf3deferred Tff7b72=
Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Te6edf3inFlightTff7b72[Te6edf3destNumTff7b72]
Tff7b72?: Te6edf3serviceScope
Tb4b4b4.Te6edf3asyncTb4b4b4(Te6edf3start Tff7b72= Te6edf3CoroutineStartTb4b4b4.Te6edf3LAZYTb4b4b4) Tb4b4b4{ Te6edf3runEnsureTb4b4b4(Te6edf3destNumTb4b4b4) Tb4b4b4}
Tb4b4b4.Te6edf3also Tb4b4b4{ Te6edf3inFlightTff7b72[Te6edf3destNumTff7b72] Tff7b72= Tffa657it Tb4b4b4}
Tb4b4b4}
Tff7b72return Tff7b72try Tb4b4b4{
Te6edf3deferredTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3mutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Tff7b72if Tb4b4b4(Te6edf3inFlightTff7b72[Te6edf3destNumTff7b72] Tff7b72=Tff7b72=Tff7b72= Te6edf3deferredTb4b4b4) Te6edf3inFlightTb4b4b4.Te6edf3removeTb4b4b4(Te6edf3destNumTb4b4b4) Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffrunEnsureTb4b4b4(Te6edf3destNumTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3EnsureSessionResult Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffEnsureRemoteAdminSession dispatching metadata request to Tffd700$Te6edf3destNumTa5d6ff" Tb4b4b4}
Tff7b72return Te6edf3withTimeoutOrNullTb4b4b4(Te6edf3UX_TIMEOUTTb4b4b4) Tb4b4b4{
T8b949e// Subscribe BEFORE dispatching so we don't miss the refresh emission.
Tff7b72val Te6edf3refreshed Tff7b72=
Te6edf3serviceScopeTb4b4b4.Te6edf3asyncTb4b4b4(Te6edf3start Tff7b72= Te6edf3CoroutineStartTb4b4b4.Te6edf3UNDISPATCHEDTb4b4b4) Tb4b4b4{
Te6edf3sessionManagerTb4b4b4.Te6edf3sessionRefreshFlowTb4b4b4.Te6edf3filter Tb4b4b4{ Tffa657it Tff7b72=Tff7b72= Te6edf3destNum Tb4b4b4}Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72try Tb4b4b4{
Te6edf3radioControllerTb4b4b4.Te6edf3refreshMetadataTb4b4b4(Te6edf3destNumTb4b4b4)
Te6edf3refreshedTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Te6edf3EnsureSessionResultTb4b4b4.Te6edf3Refreshed
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3refreshedTb4b4b4.Te6edf3cancelTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72?: Te6edf3EnsureSessionResultTb4b4b4.Te6edf3Timeout
Tb4b4b4}

Tff7b72companion Tff7b72object Tb4b4b4{
T8b949e/**
* UX deadline for surfacing a result to the user. The metadata request keeps flying after this — late responses
* still update the durable `SessionStatus` flow.
*/
Tff7b72val Te6edf3UX_TIMEOUT Tff7b72= T79c0ff1T79c0ff0.Te6edf3seconds
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.2 - Generated in 0.04s